#
# Класс для обращения к spirech.org
# Автор: Spirech Admin
# 
# Создано для треда https://spirech.org/comp/res/631.html
#
# Версия 0.1
#
# Для этой версии доступны только методы обращения к статическим страницам сайта
#


import requests
import re
from rich import print


_markup_regex = {
	re.compile(r"<strong>(?P<content>.*?)</strong>", re.DOTALL): f"[bold]%group1[/bold]",
	re.compile(r"<br\ ?/?>(?P<content>)"): f"\n", # пустая группа для унификации
	re.compile(r"<em>(?P<content>.*?)</em>", re.DOTALL): f"[italic]%group1[/italic]",
	re.compile(r"<span\ ?class=\"shoved\">(?P<content>.*?)<\/span>", re.DOTALL): f"[s]%group1[/s]",
	re.compile(r"<span\ ?class=\"underlined\">(?P<content>.*?)<\/span>", re.DOTALL): f"[u]%group1[/u]",
	re.compile(r"<sup>(?P<content>.*?)</sup>", re.DOTALL): f"^%group1 ",
	re.compile(r"<sub>(?P<content>.*?)</sub>", re.DOTALL): f"%group1",
	re.compile(r"<span\ ?class=\"quote\">(?P<content>.*?)<\/span>", re.DOTALL): f"[green]%group1[/green]",
	re.compile(r"<span\ ?class=\"spoiler\">(?P<content>.*?)<\/span>", re.DOTALL): f"(%group1)",
	re.compile(r"<pre\ lang=\".{2,3}\"\ ?><code\ ?(class=\"[a-zA-Z0-9\ ]{3,12}\"\ )?(data-lang=\"[a-zA-Z0-9\ ]{2,5}\")?>(?P<content>.*?)</code></pre>", re.DOTALL): f"[quote]%group1[/quote]",
	re.compile(r"<span\ ?class=\"acronym\"\ ?title=\"(?P<title>.*?)\">(?P<content>.*?)</span>"): f"%group1 (%title)",
}

_markup_replace = {
	"&lt;": "<",
	"&gt;": ">",
	"&hellip;": "...",
}


class Convert(object):
	'''
	Класс для внутренних методов, выполняет декоративную функцию
	'''

	def __init__(self):
		super().__init__()

	def _console_remove_markup(self, text: str) -> str:
		for p in _markup_regex:
			# для каждого regex есть свой шаблон
			text = self._replace_normarkup(text, p)

		for rp in _markup_replace:
			text = text.replace(rp, _markup_replace[rp])

		return text

	def _replace(self, text: str, markup_re: re.Pattern) -> str:
		'''Внутренний метод замены в тексте по регулярным выражениям тегом'''
		# 1 получить группу 
		# 2 подготовить замену
		# 3 заменить через replace()

		# получение группы (group1)
		haystack = markup_re.search(text)
		if not haystack: return text

		while haystack:
			# содержание тега
			content_raw = haystack.group("content")
			try:
				title = haystack.group("title")
			except:
				title = False

			# позиция в строке
			start, end = haystack.span()
			
			# финальный вариант по шаблону
			content_fin = _markup_regex[markup_re].replace(r"%group1", content_raw)

			if _markup_regex[markup_re] == "\n":
				content_fin = "\n"

			if title:
				content_fin = content_fin.replace(r"%title", title)

			# замена ТОЛЬКО в этой позиции
			text = text[:start] + content_fin + text[end:]

			# обновление
			haystack = markup_re.search(text)

		return text

	def _replace_normarkup(self, text: str, markup_re: re.Pattern) -> str:
		# получение группы (group1)
		haystack = markup_re.search(text)
		if not haystack: return text

		while haystack:
			# содержание тега
			content_raw = haystack.group("content")
			try:
				title = haystack.group("title")
			except:
				title = False

			# позиция в строке
			start, end = haystack.span()

			if title:
				content_fin = f"{content_raw}(title)"
			else:
				content_fin = content_raw

			# замена ТОЛЬКО в этой позиции
			text = text[:start] + content_fin + text[end:]

			# обновление
			haystack = markup_re.search(text)

		return text

	def _console_markup(self, text: str) -> str:
		for p in _markup_regex:
			# для каждого regex есть свой шаблон
			text = self._replace(text, p)

		for rp in _markup_replace:
			text = text.replace(rp, _markup_replace[rp])

		return text

	def _remove_markup(self, text: str) -> str:
		for p in _markup_regex:
			# для каждого regex есть свой шаблон
			text = self._replace_normarkup(text, p)

		for rp in _markup_replace:
			text = text.replace(rp, _markup_replace[rp])

		return text


class SAPI(Convert):
	'''
	Spirech API class
	'''
	def __init__(self, root="https://spirech.org", verbose=0, proxies={}):
		super().__init__()

		headers = {
			"Accept": "*/*",
			"Accept-Encoding": "gzip",
			"User-Agent": "spirech-cli v0.1",
			"Cache-Control": "no-cache",
			"Accept-Language": "en-US;q=0.8,en;q=0.5",
		}

		self.root = root
		self.api_url = root + "/api.php"
		self.session = requests.Session()
		self.session.headers.update(headers)
		self.session.proxies.update(proxies)

	def isonline(self) -> bool:
		response = self.session.head(self.root)
		return response.status_code == 200

	# def iswork(self) -> bool:
	# 	'''Проверка соединения'''
	# 	params = {
	# 		"task": "ping",
	# 	}

	# 	response = self.session.get(self.api_url, params=params).json()

	# 	return response.get("status") == "OK"

	# def register(self) -> bool:
	# 	'''Временная анонимная регистрация клиента в системе'''
	# 	params = {
	# 		"task": "register",
	# 	}

	# 	response = self.session.get(self.api_url, params=params).json()

	# 	if response.get("status") == "OK":
	# 		self.session.headers["X-User-Id"] = response.get("id")
	# 		self.session.headers["X-User-Hash"] = response.get("hash")
	# 		return True
	# 	else:
	# 		return False

	# def is_registered(self) -> bool:
	# 	'''Используется для дебага'''
	# 	params = {
	# 		"task": "am_i_reg",
	# 	}

	# 	response = self.session.get(self.api_url, params=params).json()

	# 	if response.get("status") == "OK" and response.get("response"):
	# 		return True
	# 	else:
	# 		return False

	def get_thread_raw(self, board: str, thread: complex) -> complex:
		'''Получение треда как есть'''
		response = self.session.get(self.root + f"/{board}/res/{str(thread)}.json")

		if response.status_code != 200:
			return False
		else:
			return response.json()

	def get_thread_nomarkup(self, board: str, thread: complex) -> complex:
		'''Получение треда без разметки'''
		response = self.get_thread_raw(board, thread)
		if not response: return False

		# замена html-разметки на обычный текст
		for i in range(0, len(response["posts"])):
			if not response["posts"][i].get("com"): continue
		
			text = response["posts"][i]["com"]

			# пре-замена тега <br>
			text = self._replace(text, re.compile(r"<br\ ?/?>(?P<content>)"))

			# замена тегов
			text = self._console_remove_markup(text)

			response["posts"][i]["com"] = text

		return response

	def get_thread_markup(self, board: str, thread: complex) -> complex:
		'''Получение треда с консольной разметкой'''
		response = self.get_thread_raw(board, thread)
		if not response: return False

		# замена html-разметки на частичный аналог консольной
		for i in range(0, len(response["posts"])):
			if not response["posts"][i].get("com"): continue

			text = response["posts"][i]["com"]

			# замена тегов
			text = self._console_markup(text)
			
			response["posts"][i]["com"] = text

		return response


	def get_board_raw(self, board: str, page=0) -> complex:
		'''Получение доски как есть'''
		response = self.session.get(self.root + f"/{board}/{str(page)}.json")
		if response.status_code != 200:
			return False
		else:
			return response.json()

	def get_board_nomarkup(self, board: str, page=0) -> complex:
		'''Получение доски без разметки'''
		response = self.get_board_raw(board, page=page)
		if not response: return False

		# первое вхождение в список тредов
		for threadIndex in range(0, len(response["threads"])):
			# второе вхождение в список постов
			for postIndex in range(0, len(response["threads"][threadIndex]["posts"])):
				text = response["threads"][threadIndex]["posts"][postIndex]["com"]
				# пре-замена тега <br>
				text = self._replace(text, re.compile(r"<br\ ?/?>(?P<content>)"))

				# замена тегов
				text = self._console_remove_markup(text)
				response["threads"][threadIndex]["posts"][postIndex]["com"] = text
		return response

	def get_board_markup(self, board: str, page=0) -> complex:
		'''Получение доски с консольной разметкой'''
		response = self.get_board_raw(board, page=page)
		if not response: return False

		# первое вхождение в список тредов
		for threadIndex in range(0, len(response["threads"])):
			# второе вхождение в список постов
			for postIndex in range(0, len(response["threads"][threadIndex]["posts"])):
				text = response["threads"][threadIndex]["posts"][postIndex]["com"]
				
				# замена тегов
				text = self._console_markup(text)
				response["threads"][threadIndex]["posts"][postIndex]["com"] = text
		return response



	# def prepare_post(self, board: str, thread: complex) -> complex:
	# 	pass

	# def submit_post(self):
	# 	pass

	# def get_ascii_captcha(self):
	# 	pass

	def __exit__(self, *args, **kwargs):
		self.session.close()


if __name__ == '__main__':
	pass